// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef BASE_TEST_MOCK_LOG_H_
#define BASE_TEST_MOCK_LOG_H_

#include <stddef.h>

#include <string>

#include "base/logging.h"
#include "base/macros.h"
#include "base/synchronization/lock.h"
#include "testing/gmock/include/gmock/gmock.h"

namespace base {
namespace test {

    // A MockLog object intercepts LOG() messages issued during its lifespan.  Using
    // this together with gMock, it's very easy to test how a piece of code calls
    // LOG().  The typical usage:
    //
    //   TEST(FooTest, LogsCorrectly) {
    //     MockLog log;
    //
    //     // We expect the WARNING "Something bad!" exactly twice.
    //     EXPECT_CALL(log, Log(WARNING, _, "Something bad!"))
    //         .Times(2);
    //
    //     // We allow foo.cc to call LOG(INFO) any number of times.
    //     EXPECT_CALL(log, Log(INFO, HasSubstr("/foo.cc"), _))
    //         .Times(AnyNumber());
    //
    //     log.StartCapturingLogs();  // Call this after done setting expectations.
    //     Foo();  // Exercises the code under test.
    //   }
    //
    // CAVEAT: base/logging does not allow a thread to call LOG() again when it's
    // already inside a LOG() call.  Doing so will cause a deadlock.  Therefore,
    // it's the user's responsibility to not call LOG() in an action triggered by
    // MockLog::Log().  You may call RAW_LOG() instead.
    class MockLog {
    public:
        // Creates a MockLog object that is not capturing logs.  If it were to start
        // to capture logs, it could be a problem if some other threads already exist
        // and are logging, as the user hasn't had a chance to set up expectation on
        // this object yet (calling a mock method before setting the expectation is
        // UNDEFINED behavior).
        MockLog();

        // When the object is destructed, it stops intercepting logs.
        ~MockLog();

        // Starts log capturing if the object isn't already doing so.
        // Otherwise crashes.
        void StartCapturingLogs();

        // Stops log capturing if the object is capturing logs.  Otherwise crashes.
        void StopCapturingLogs();

        // Log method is invoked for every log message before it's sent to other log
        // destinations (if any).  The method should return true to signal that it
        // handled the message and the message should not be sent to other log
        // destinations.
        MOCK_METHOD5(Log,
            bool(int severity,
                const char* file,
                int line,
                size_t message_start,
                const std::string& str));

    private:
        // The currently active mock log.
        static MockLog* g_instance_;

        // Lock protecting access to g_instance_.
        static Lock g_lock;

        // Static function which is set as the logging message handler.
        // Called once for each message.
        static bool LogMessageHandler(int severity,
            const char* file,
            int line,
            size_t message_start,
            const std::string& str);

        // True if this object is currently capturing logs.
        bool is_capturing_logs_;

        // The previous handler to restore when the MockLog is destroyed.
        logging::LogMessageHandlerFunction previous_handler_;

        DISALLOW_COPY_AND_ASSIGN(MockLog);
    };

} // namespace test
} // namespace base

#endif // BASE_TEST_MOCK_LOG_H_
